package com.example.auth.controller;

import com.example.auth.dto.ChatMessageDTO;
import com.example.auth.dto.ConversationDTO;
import com.example.auth.entity.User;
import com.example.auth.util.UserContext;
import com.example.auth.service.ChatService;
import com.example.auth.service.UserService;
import org.springframework.http.ResponseEntity;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.web.bind.annotation.*;

import java.security.Principal;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/api/chat")
public class ChatController {

    private final ChatService chatService;
    private final UserService userService;
    private final UserContext userContext;

    public ChatController(ChatService chatService, UserService userService, UserContext userContext) {
        this.chatService = chatService;
        this.userService = userService;
        this.userContext = userContext;
    }

    // REST API Endpoints

    /**
     * Get all conversations for the current user
     */
    @GetMapping("/conversations")
    public ResponseEntity<List<ConversationDTO>> getConversations() {
        User currentUser = userContext.getCurrentUser();
        List<ConversationDTO> conversations = chatService.getConversationsForUser(currentUser);
        return ResponseEntity.ok(conversations);
    }

    /**
     * Get the conversation with the specified user
     */
    @GetMapping("/conversations/{userId}")
    public ResponseEntity<ConversationDTO> getConversationWithUser(@PathVariable Long userId) {
        User currentUser = userContext.getCurrentUser();
        User otherUser = userService.findById(userId);

        ConversationDTO conversation = chatService.getOrCreateConversation(currentUser, otherUser);
        return ResponseEntity.ok(conversation);
    }

    /**
     * Get messages for the specified conversation (paginated)
     */
    @GetMapping("/conversations/{conversationId}/messages")
    public ResponseEntity<List<ChatMessageDTO>> getMessagesForConversation(
            @PathVariable Long conversationId,
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "20") int size
    ) {
        List<ChatMessageDTO> messages = chatService.getMessagesForConversation(conversationId, page, size);
        return ResponseEntity.ok(messages);
    }

    /**
     * Get messages before the specified message ID (load more)
     */
    @GetMapping("/conversations/{conversationId}/messages/before/{messageId}")
    public ResponseEntity<List<ChatMessageDTO>> getMessagesBeforeId(
            @PathVariable Long conversationId,
            @PathVariable Long messageId,
            @RequestParam(defaultValue = "20") int size
    ) {
        List<ChatMessageDTO> messages = chatService.getMessagesBeforeId(conversationId, messageId, size);
        return ResponseEntity.ok(messages);
    }

    /**
     * Mark all messages in the conversation as read
     */
    @PostMapping("/conversations/{conversationId}/read")
    public ResponseEntity<Map<String, Integer>> markConversationAsRead(@PathVariable Long conversationId) {
        User currentUser = userContext.getCurrentUser();
        int updatedCount = chatService.markConversationAsRead(conversationId, currentUser.getId());
        return ResponseEntity.ok(Map.of("updatedCount", updatedCount));
    }

    /**
     * Get the number of unread messages for the current user
     */
    @GetMapping("/unread/count")
    public ResponseEntity<Map<String, Integer>> getUnreadMessageCount() {
        User currentUser = userContext.getCurrentUser();
        int count = chatService.getUnreadMessageCount(currentUser.getId());
        return ResponseEntity.ok(Map.of("count", count));
    }

    /**
     * Get the number of conversations with unread messages for the current user
     */
    @GetMapping("/unread/conversations/count")
    public ResponseEntity<Map<String, Integer>> getUnreadConversationCount() {
        User currentUser = userContext.getCurrentUser();
        int count = chatService.getUnreadConversationCount(currentUser.getId());
        return ResponseEntity.ok(Map.of("count", count));
    }

    // WebSocket Endpoints

    /**
     * Send a private message
     */
    @MessageMapping("/chat.sendMessage")
    public void sendMessage(@Payload Map<String, Object> payload, Principal principal) {
        try {
            if (principal == null) {
                System.err.println("Failed to send message: Principal is null");
                return;
            }

            String principalName = principal.getName();
            System.out.println("Handling WebSocket message sending request, Principal: " + principalName);

            Long senderId = null;

            try {
                senderId = Long.valueOf(principalName);
                System.out.println("Successfully converted Principal name to senderId: " + senderId);

                // Verify if the user ID exists
                User sender = userService.findById(senderId);
                if (sender == null) {
                    String errorMsg = "Sender does not exist (ID: " + senderId + ")";
                    System.err.println(errorMsg);
                    chatService.sendErrorNotification(senderId, errorMsg);
                    return;
                }
            } catch (NumberFormatException e) {
                System.err.println("Failed to send message: Principal name is not a valid user ID - " + principalName + ", Exception: " + e.getMessage());
                return;
            }

            if (payload == null) {
                System.err.println("Failed to send message: Payload is null");
                return;
            }

            Object recipientIdObj = payload.get("recipientId");
            Object contentObj = payload.get("content");

            if (recipientIdObj == null) {
                System.err.println("Failed to send message: Missing recipient ID");
                chatService.sendErrorNotification(senderId, "Missing recipient ID");
                return;
            }

            if (contentObj == null) {
                System.err.println("Failed to send message: Missing message content");
                chatService.sendErrorNotification(senderId, "Missing message content");
                return;
            }

            Long recipientId;
            try {
                recipientId = Long.valueOf(recipientIdObj.toString());

                // Verify if the recipient ID exists
                User recipient = userService.findById(recipientId);
                if (recipient == null) {
                    String errorMsg = "Recipient does not exist (ID: " + recipientId + ")";
                    System.err.println(errorMsg);
                    chatService.sendErrorNotification(senderId, errorMsg);
                    return;
                }
            } catch (NumberFormatException e) {
                String errorMsg = "Recipient ID is not a valid number - " + recipientIdObj;
                System.err.println(errorMsg + ", Exception: " + e.getMessage());
                chatService.sendErrorNotification(senderId, errorMsg);
                return;
            }

            String content = contentObj.toString();

            // Check if there is file information
            String fileUrl = payload.get("fileUrl") != null ? payload.get("fileUrl").toString() : null;
            String fileName = payload.get("fileName") != null ? payload.get("fileName").toString() : null;
            String fileType = payload.get("fileType") != null ? payload.get("fileType").toString() : null;
            Long fileSize = payload.get("fileSize") != null ? Long.valueOf(payload.get("fileSize").toString()) : null;

            System.out.println("Preparing to send message via WebSocket, Sender ID: " + senderId + ", Recipient ID: " + recipientId);

            try {
                if (fileUrl != null) {
                    // Send file message
                    chatService.sendMessage(senderId, recipientId, content, fileUrl, fileName, fileType, fileSize);
                } else {
                    // Send regular text message
                    chatService.sendMessage(senderId, recipientId, content);
                }
            } catch (RuntimeException e) {
                System.err.println("Service layer threw an exception while sending message: " + e.getMessage());
                chatService.sendErrorNotification(senderId, e.getMessage());
                throw e;
            }
        } catch (Exception e) {
            System.err.println("Exception occurred while processing WebSocket message: " + e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * Receive messages sent from the Web API
     */
    @PostMapping("/send")
    public ResponseEntity<ChatMessageDTO> sendMessageRest(
            @RequestBody Map<String, Object> payload
    ) {
        User currentUser = userContext.getCurrentUser();
        Long recipientId = Long.valueOf(payload.get("recipientId").toString());
        String content = payload.get("content") != null ? payload.get("content").toString() : "";

        // Check if there is file information
        String fileUrl = payload.get("fileUrl") != null ? payload.get("fileUrl").toString() : null;
        String fileName = payload.get("fileName") != null ? payload.get("fileName").toString() : null;
        String fileType = payload.get("fileType") != null ? payload.get("fileType").toString() : null;
        Long fileSize = payload.get("fileSize") != null ? Long.valueOf(payload.get("fileSize").toString()) : null;

        ChatMessageDTO message;
        if (fileUrl != null) {
            // Send file message
            message = chatService.sendMessage(currentUser.getId(), recipientId, content, fileUrl, fileName, fileType, fileSize);
        } else {
            // Send regular text message
            message = chatService.sendMessage(currentUser.getId(), recipientId, content);
        }

        return ResponseEntity.ok(message);
    }

    /**
     * Connect user
     */
    @MessageMapping("/chat.connect")
    public void connect(SimpMessageHeaderAccessor headerAccessor, Principal principal) {
        // The user has already been authenticated via JWT in WebSocketConfig and the principal has been set
        try {
            if (principal != null) {
                System.out.println("User connected to WebSocket, Principal: " + principal.getName());
            } else {
                System.err.println("User connected to WebSocket, but Principal is null");
            }
        } catch (Exception e) {
            System.err.println("Exception occurred while handling WebSocket connection: " + e.getMessage());
        }
    }
}